package handler

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"mime/multipart"
	"net/http"
	"strconv"
	"strings"
	"time"
	"yunpan/db"
	"yunpan/logger"
	"yunpan/meta"
	"yunpan/pool"
	"yunpan/store/ceph"

	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
)

type uploadTaskResult struct {
	isFileExist bool
	err         error
}

// validateUploadRequest 验证上传请求
func validateUploadRequest(c *gin.Context) (string, error) {
	username := c.MustGet("username").(string)
	if username == "" {
		logger.Error("用户未登录",
			zap.String("handler", "UploadHandler"),
			zap.String("error", "empty username"),
		)
		return "", errors.New("用户未登录")
	}
	return username, nil
}

// readUploadFile 读取上传的文件
func readUploadFile(c *gin.Context) ([]byte, *multipart.FileHeader, error) {
	file, header, err := c.Request.FormFile("file")
	if err != nil {
		return nil, nil, fmt.Errorf("获取文件失败: %v", err)
	}
	defer file.Close()

	var buffer bytes.Buffer
	if _, err = io.Copy(&buffer, file); err != nil {
		return nil, nil, fmt.Errorf("读取文件内容失败: %v", err)
	}

	return buffer.Bytes(), header, nil
}

// calculateFileHash 计算文件哈希
func calculateFileHash(hashPool *pool.HashWorkerPool, fileData []byte) (string, error) {
	hashTask := pool.NewHashTask(fileData, 1)
	if err := hashPool.Submit(hashTask); err != nil {
		return "", fmt.Errorf("提交哈希计算任务失败: %v", err)
	}

	fileSha1, err := hashTask.GetResult()
	if err != nil {
		return "", fmt.Errorf("计算文件哈希失败: %v", err)
	}

	return fileSha1, nil
}

// createFileMeta 创建文件元信息
func createFileMeta(header *multipart.FileHeader, fileSha1 string) meta.FileMeta {
	return meta.FileMeta{
		FileName:       header.Filename,
		Location:       "tmp/" + header.Filename,
		FileSize:       header.Size,
		UploadAt:       time.Now().Format("2006-01-02 15:04:05"),
		FileSha1:       fileSha1,
		ReferenceCount: 1, // 默认引用计数为1
		IsOriginal:     true,
	}
}

// processUploadTasks 处理上传任务
func processUploadTasks(username string, fileMeta meta.FileMeta, fileData []byte,
	parentPath, relativePath string, header *multipart.FileHeader,
	dbPool *pool.DBWorkerPool, cephPool *pool.CephWorkerPool) error {
	result := make(chan uploadTaskResult, 1)

	// 创建数据库任务
	dbTask := pool.NewDBTask(
		"上传文件-更新数据库",
		3,
		func() error {

			fileExist, err := db.CheckFileExists(username, fileMeta.FileSha1)
			if err != nil {
				logger.Error("检查文件存在失败",
					zap.String("username", username),
					zap.String("filehash", fileMeta.FileSha1),
					zap.Error(err))
				result <- uploadTaskResult{err: err}
				return err
			}
			if fileExist {
				fileMeta.IsOriginal = false
			}
			// 更新文件元信息 -- 暂时不更新（这里主要用于秒传功能，根据需要来更新）
			// if !meta.UpdateFileMetaDB(fileMeta) {
			// 	return errors.New("failed to update file meta")
			// }
			if !db.OnUserFileUploadFinished(username, fileMeta.FileSha1,
				fileMeta.FileName, fileMeta.FileSize, parentPath, fileMeta.IsOriginal) {
				result <- uploadTaskResult{err: err}
				return errors.New("failed to update user file")
			}
			result <- uploadTaskResult{isFileExist: fileExist}
			return nil
		},
	)

	if err := dbPool.Submit(dbTask); err != nil {
		return fmt.Errorf("提交数据库任务失败: %v", err)
	}
	// 等待数据库任务的结果
	taskResult := <-result
	if taskResult.err != nil {
		return taskResult.err
	}
	if !taskResult.isFileExist {
		// 创建存储任务
		fullPath := constructFullPath(parentPath, relativePath, header.Filename)
		fullPath = strings.TrimPrefix(fullPath, "/")

		cephTask := pool.NewCephTask(
			username,
			"上传文件",
			3,
			func(conn *ceph.StorageSession) error {
				return conn.UploadObject(fullPath, fileData)
			},
		)

		if err := cephPool.Submit(cephTask); err != nil {
			return fmt.Errorf("提交存储任务失败: %v", err)
		}

		// 等待所有任务完成
		if err := waitForTasks(dbTask, cephTask); err != nil {
			return fmt.Errorf("上传失败: %v", err)
		}
		return nil
	}
	// 秒传：只需要等待数据库任务完成
	if err := waitForTasks(dbTask); err != nil {
		return fmt.Errorf("秒传失败: %v", err)
	}

	return nil
}

// UploadHandler 处理文件上传
func UploadHandler(c *gin.Context, hashPool *pool.HashWorkerPool,
	dbPool *pool.DBWorkerPool, cephPool *pool.CephWorkerPool) {

	// 1. 验证请求
	username, err := validateUploadRequest(c)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"code": 400,
			"msg":  err.Error(),
		})
		return
	}

	// 2. 读取文件
	fileData, header, err := readUploadFile(c)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"code": 400,
			"msg":  err.Error(),
		})
		return
	}

	// 3. 计算文件哈希
	fileSha1, err := calculateFileHash(hashPool, fileData)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"code": 500,
			"msg":  err.Error(),
		})
		return
	}

	// 4. 创建文件元信息
	fileMeta := createFileMeta(header, fileSha1)

	// 5. 处理上传任务
	parentPath := c.DefaultPostForm("path", "/")
	relativePath := c.DefaultPostForm("webkitRelativePath", "")

	if err := processUploadTasks(username, fileMeta, fileData,
		parentPath, relativePath, header, dbPool, cephPool); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"code": 500,
			"msg":  err.Error(),
		})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"code": 200,
		"msg":  "Upload succeeded",
	})
}

// 构造完整路径
func constructFullPath(parentPath, relativePath, filename string) string {
	if relativePath != "" {
		if parentPath == "/" {
			return "/" + relativePath
		}
		return parentPath + relativePath
	}

	if parentPath == "/" {
		return "/" + filename
	}
	return parentPath + filename
}

// 等待任务完成
func waitForTasks(tasks ...pool.Task) error {
	for _, task := range tasks {
		if t, ok := task.(pool.WaitableTask); ok {
			if err := t.Wait(); err != nil {
				return err
			}
		}
	}
	return nil
}

// FileQueryHandler 查询批量的文件元信息
func FileQueryHandler(c *gin.Context) {
	limitCnt, _ := strconv.Atoi(c.Query("limit"))
	username := c.MustGet("username").(string)

	fileMetas, err := db.QueryUserFileMetas(username, limitCnt)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"code": 500,
			"msg":  "Failed to query file metas",
		})
		return
	}

	data := make([]gin.H, 0)
	for _, meta := range fileMetas {
		data = append(data, gin.H{
			"FileHash":   meta.FileHash,
			"FileName":   meta.FileName,
			"FileSize":   meta.FileSize,
			"UpdateTime": meta.LastUpdate.Format("2006-01-02 15:04:05"),
			"UploadTime": meta.UploadAt.Format("2006-01-02 15:04:05"),
		})
	}

	c.JSON(http.StatusOK, gin.H{
		"code": 200,
		"data": data,
	})
}

// FileMetaUpdateHandler 更新文件元信息
func FileMetaUpdateHandler(c *gin.Context) {
	username := c.MustGet("username").(string)

	opType := c.Query("op")
	fileSha1 := c.Query("filehash")
	newFileName := c.Query("filename")

	if opType != "0" || len(fileSha1) == 0 || len(newFileName) == 0 {
		c.JSON(http.StatusBadRequest, gin.H{
			"code": 400,
			"msg":  "Invalid parameters",
		})
		return
	}

	if err := db.RenameUserFile(username, fileSha1, newFileName); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"code": 500,
			"msg":  "Update failed: " + err.Error(),
		})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"code": 200,
		"msg":  "Update succeeded",
	})
}

// DownloadHandler 文件下载
// DownloadHandler 文件下载
// DownloadHandler 文件下载
func DownloadHandler(c *gin.Context) {
	username := c.MustGet("username").(string)
	fileId := c.Param("id")

	// 1. 只获取当前用户的文件信息
	userFile, err := db.GetOriginalFile(fileId)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"code": 500,
			"msg":  "获取文件信息失败",
		})
		return
	}

	// 2. 从当前用户的 Ceph 存储下载
	cephConn, err := ceph.GetCephConn(username)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"code": 500,
			"msg":  "获取存储连接失败",
		})
		return
	}

	fullPath := constructFullPath(userFile.ParentPath, "", userFile.FileName)
	fullPath = strings.TrimPrefix(fullPath, "/")

	data, err := cephConn.DownloadObject(fullPath)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"code": 500,
			"msg":  "下载文件失败",
		})
		return
	}

	c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", userFile.FileName))
	c.Data(http.StatusOK, "application/octet-stream", data)
}
